import cv2 as cv
import numpy as np


# 一幅图像是由不同大小的灰度级像素值构成的，可以把不同的大小想象成不同高度的山脉，接着在地表（就是从像素灰度级0开始）向这个山脉地脉注入水，那么当一个山脉与另一个山脉将要融合的线上就是图像的边界，当水注入最高山脉后形成的现象就是整幅图的边界。
def water_shed(image):
    blurred = cv.pyrMeanShiftFiltering(image, 10, 30)
    gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)
    ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
    cv.imshow('binary', binary)
    kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
    open_binary = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel, iterations=2)  # 2次开操作，去除噪声
    cv.imshow('open_binary', open_binary)
    dilate_op = cv.dilate(open_binary, kernel, iterations=3)  # 3次膨胀。
    # 获得掩码需要知道图像的前景和背景，在这里我的理解是：前景就是有硬币的图像，背景就是除硬币之前的区域。我们要确保我们获得的背景图中不包含前景图的区域，通过腐蚀操作，将硬币的区域放大，那么剩下的就一定是背景区域了，即可以确定背景图
    cv.imshow('dilate_op', dilate_op)
    dist = cv.distanceTransform(open_binary, cv.DIST_L2, 3)  # 可用来实现目标细化、骨架提取、形状插值及匹配、粘连物体的分离等
    # 距离变换是针对二值图像的一种变换。在二维空间中，一幅二值图像可以认为仅仅包含目标和背景两种像素，目标的像素值为1，背景的像素值为0；
    # 距离图像是图像中每个像素的灰度值为该像素与距其最近的背景像素间的距离（就是原二值图像中像素值为1的像素点与最近的像素点为0的像素点之间的距离）
    # DIST_L1:曼哈顿距离，DIST_L2：欧氏距离,masksize:跟卷积一样
    dist_norm = cv.normalize(dist, 0, 1.0, cv.NORM_MINMAX)  # 归一化，其中第二三个参数分别是上下限，第四个参数是归一化选择的数学公式
    cv.imshow('distance', dist_norm*50)
    # 由于掩码是一幅二值图像，所以经过距离变化后还需要将图像进行二值化
    # 这样子的结果图像中像素点为1的区域就一定是硬币的位置，即前景图
    ret, surface_image = cv.threshold(dist, dist.max()*0.65, 255, cv.THRESH_BINARY)
    cv.imshow('surface_image', surface_image)
    # 现在我们已经知道原图像的前景图和背景图了，但是还有一些区域是我们所不知道的就是原图像中硬币与硬币之间相连的那些或者叠加的区域，即边界，通过背景图减去前景图可以大概的获得这些未知的边界
    surface = np.uint8(surface_image)
    fringe = cv.subtract(dilate_op, surface)
    cv.imshow('fringe', fringe)
    ret, markers = cv.connectedComponents(surface)  # connectedComponents函数可以使图像中标记背景像素点为0，飞背景像素点从1开始累加分别标记
    print(ret)
    cv.imshow('markers', markers)
    markers = markers+1  # 在所有的标签上加1，以确保背景不是0，而是1
    markers[fringe == 255] = 0  # 将边缘区域标记为0
    markers = cv.watershed(image, markers)  # 分水岭算法
    image[markers == -1] = [0, 0, 255]  # 经过分水岭分割算法后，边界处会标记为-1
    cv.imshow('7-result', image)


src = cv.imread('imgs/test011.jpg')
cv.imshow("input_image", src)
water_shed(src)
cv.waitKey(0)
cv.destroyAllWindows()